#include "stdafx.h"
#include "event/rwlock.hpp"

namespace JHCPP
{
	namespace event
	{
	#if defined(JH_OS_WINDOWS)
		CRWLockImpl::CRWLockImpl()
		: m_readers(0), 
		m_writersWaiting(0), 
		m_writers(0)
		{
			m_mutex = ::CreateMutex(NULL, FALSE, NULL);
			if (m_mutex == NULL)
				throw SystemException("cannot create reader/writer lock");

			m_readEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL);
			if (m_readEvent == NULL)
				throw SystemException("cannot create reader/writer lock");

			m_writeEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL);
			if (m_writeEvent == NULL)
				throw SystemException("cannot create reader/writer lock");
		}

		CRWLockImpl::~CRWLockImpl()
		{
			::CloseHandle(m_mutex);
			::CloseHandle(m_readEvent);
			::CloseHandle(m_writeEvent);
		}

		void CRWLockImpl::readLockImpl()
		{
			HANDLE h[2];
			h[0] = m_mutex;
			h[1] = m_readEvent;
			switch (::WaitForMultipleObjects(2, h, TRUE, INFINITE))
			{
			case WAIT_OBJECT_0:
			case WAIT_OBJECT_0 + 1:
				++m_readers;
				::ResetEvent(m_writeEvent);
				::ReleaseMutex(m_mutex);
				jh_assert_dbg(m_writers == 0);
				break;
			default:
				throw SystemException("cannot lock reader/writer lock");
			}
		}

		bool CRWLockImpl::tryReadLockImpl()
		{
			for (;;)
			{
				if (m_writers != 0 || m_writersWaiting != 0)
					return false;

				DWORD result = tryReadLockOnce();
				switch (result)
				{
				case WAIT_OBJECT_0:
				case WAIT_OBJECT_0 + 1:
					return true;
				case WAIT_TIMEOUT:
					continue; // try again
				default:
					throw SystemException("cannot lock reader/writer lock");
				}
			}
		}

		void CRWLockImpl::writeLockImpl()
		{
			addWriter();
			HANDLE h[2];
			h[0] = m_mutex;
			h[1] = m_writeEvent;
			switch (::WaitForMultipleObjects(2, h, TRUE, INFINITE))
			{
			case WAIT_OBJECT_0:
			case WAIT_OBJECT_0 + 1:
				--m_writersWaiting;
				++m_readers;
				++m_writers;
				::ResetEvent(m_readEvent);
				::ResetEvent(m_writeEvent);
				::ReleaseMutex(m_mutex);
				jh_assert_dbg(m_writers == 1);
				break;
			default:
				removeWriter();
				throw SystemException("cannot lock reader/writer lock");
			}
		}

		bool CRWLockImpl::tryWriteLockImpl()
		{
			addWriter();
			HANDLE h[2];
			h[0] = m_mutex;
			h[1] = m_writeEvent;
			switch (::WaitForMultipleObjects(2, h, TRUE, 1))
			{
			case WAIT_OBJECT_0:
			case WAIT_OBJECT_0 + 1:
				--m_writersWaiting;
				++m_readers;
				++m_writers;
				::ResetEvent(m_readEvent);
				::ResetEvent(m_writeEvent);
				::ReleaseMutex(m_mutex);
				jh_assert_dbg(m_writers == 1);
				return true;
			case WAIT_TIMEOUT:
				removeWriter();
				return false;
			default:
				removeWriter();
				throw SystemException("cannot lock reader/writer lock");
			}
		}

		void CRWLockImpl::unlockImpl()
		{
			switch (::WaitForSingleObject(m_mutex, INFINITE))
			{
			case WAIT_OBJECT_0:
				m_writers = 0;
				if (m_writersWaiting == 0) ::SetEvent(m_readEvent);
				if (--m_readers == 0) ::SetEvent(m_writeEvent);
				::ReleaseMutex(m_mutex);
				break;
			default:
				throw SystemException("cannot unlock reader/writer lock");
			}
		}

		void CRWLockImpl::addWriter()
		{
			switch (::WaitForSingleObject(m_mutex, INFINITE))
			{
			case WAIT_OBJECT_0:
				if (++m_writersWaiting == 1) ::ResetEvent(m_readEvent);
				::ReleaseMutex(m_mutex);
				break;
			default:
				throw SystemException("cannot lock reader/writer lock");
			}
		}

		void CRWLockImpl::removeWriter()
		{
			switch (::WaitForSingleObject(m_mutex, INFINITE))
			{
			case WAIT_OBJECT_0:
				if (--m_writersWaiting == 0 && m_writers == 0) ::SetEvent(m_readEvent);
				::ReleaseMutex(m_mutex);
				break;
			default:
				throw SystemException("cannot lock reader/writer lock");
			}
		}

		DWORD CRWLockImpl::tryReadLockOnce()
		{
			HANDLE h[2];
			h[0] = m_mutex;
			h[1] = m_readEvent;
			DWORD result = ::WaitForMultipleObjects(2, h, TRUE, 1); 
			switch (result)
			{
			case WAIT_OBJECT_0:
			case WAIT_OBJECT_0 + 1:
				++m_readers;
				::ResetEvent(m_writeEvent);
				::ReleaseMutex(m_mutex);
				jh_assert_dbg(m_writers == 0);
				return result;
			case WAIT_TIMEOUT:
				return result;
			default:
				throw SystemException("cannot lock reader/writer lock");
			}
		}
	#elif defined(JH_OS_LINUX)
		CRWLockImpl::CRWLockImpl()
		{
			if (pthread_rwlock_init(&m_rwl, NULL))
				throw SystemException("cannot create reader/writer lock");
		}

		CRWLockImpl::~CRWLockImpl()
		{
			pthread_rwlock_destroy(&m_rwl);
		}

		void CRWLockImpl::readLockImpl()
		{
			if (pthread_rwlock_rdlock(&m_rwl)) 
				throw SystemException("cannot lock reader/writer lock");
		}

		bool CRWLockImpl::tryReadLockImpl()
		{
			int rc = pthread_rwlock_tryrdlock(&m_rwl);
			if (rc == 0)
				return true;
			else if (rc == EBUSY)
				return false;
			else
				throw SystemException("cannot lock reader/writer lock");

		}

		void CRWLockImpl::writeLockImpl()
		{
			if (pthread_rwlock_wrlock(&m_rwl)) 
				throw SystemException("cannot lock reader/writer lock");
		}

		bool CRWLockImpl::tryWriteLockImpl()
		{
			int rc = pthread_rwlock_trywrlock(&m_rwl);
			if (rc == 0)
				return true;
			else if (rc == EBUSY)
				return false;
			else
				throw SystemException("cannot lock reader/writer lock");

		}

		void CRWLockImpl::unlockImpl()
		{
			if (pthread_rwlock_unlock(&m_rwl))
				throw SystemException("cannot unlock mutex");
		}

	#endif


	}//end of namespace event
}//end of namespace JHCPP
